#pragma once

#include<unordered_set>
#include<openssl/ssl.h>

//单个socket内线程安全 外部传入参数无法保证 继承后重写析构函数以实现内存托管
class SSLServer{
	static const unsigned int CACHESIZE=4096;
	public:
	struct ThreadInfo;
	private:
	ThreadInfo* threads;
	bool running;
	#ifndef _WIN32
	int epollfd;
	#endif
	SSL_CTX* ctx;
	//退出前断开socket连接并设置exited
	ThreadInfo* matchleastthread(size_t maxconnect);
	protected:
	virtual ~SSLServer();
	unsigned int nthread;

	public:
	unsigned int threadcount();
	static void sleep();
	struct DataInfo;
	static bool emptysendback(SSLServer*,ThreadInfo*,DataInfo*,void*);
	//返回false则这轮保留并重置状态 会阻塞后续数据 void*为设置的参数 char*为设置的缓冲(除了entry使用静态内存)
	typedef bool(*DataCallback)(SSLServer*,ThreadInfo*,DataInfo*,void*);
	typedef void(*LostCallback)(SSLServer*,ThreadInfo*,DataInfo*,void*);
	typedef struct{
		char* src;
		size_t size;
		DataCallback callback;
		void* arg;
	}DataPack;
	/*
	entrysize为默认接收入口的缓冲区大小
	如果到达waiting时仍未完成读取则转向其它用户
	nthread==-1则使用硬件线程数 要求至少为2
	maxmissing*waiting为最大等待帧
	超出maxpending的包会被舍弃
	maxconnect为单一线程可建立的最大连接数
	onlost在自动断线时调用(包括服务器关闭时)
	使用openssl生成的.pem文件
	*/
	int run(const char* keypath,const char* certpath,const char* address,
	unsigned int _port,unsigned int entrysize,DataCallback entryback,
	void* entryarg,LostCallback onlost,void* lostarg,unsigned int nthread=2,
	unsigned int maxpending=50,time_t timeout=120,size_t maxconnect=1000);
	//调用后会在一轮任务处理结束时关闭
	void closeserver();
	//跨线程不可再入 将数据加入发送队列 全部发送完成后调用回调函数(如有)
	static void senddata(ThreadInfo* belong,DataInfo* s,DataPack p);
	//跨线程不可再入 将数据接收加入队列 全部接收完成后调用回调函数(如有)
	static void recvdata(ThreadInfo* belong,DataInfo* s,DataPack p);
	//如果是有效请求调用以重置断线检测 禁止从不同线程调用
	static void resettime(DataInfo* s);
	//线程安全 操作期间阻塞等待其它线程完成一轮循环
	void broadcast(ThreadInfo* belong,DataPack p,std::unordered_set<DataInfo*>& excepts);
	//表中移除目标并触发onlost后关闭连接
	static void closeclient(ThreadInfo* belong,DataInfo* s);
	//常量 由框架管理内存
	static const char* getip(DataInfo* info);
	//保存TLS会话以便加速下次通信（出于安全性禁止有写操作的请求使用） 请勿重复调用
	static void keepsession(DataInfo* info);
	//由接收连接的主线程调用 可用于数据库读写硬盘场景
	virtual void update();
	//获取默认回调的缓冲区数据 框架托管内存
	static char* getbuffer(DataInfo* from);
	static bool openlib();
	static void closelib();
	static unsigned int threadid(ThreadInfo* from);
	private:
	static int _run(SSLServer* s,ThreadInfo* info,unsigned int entrysize,
	DataCallback entryback,void* entryarg,LostCallback onlost,
	void* lostarg,unsigned int maxpending,time_t timeout);
};